package cn.yshujia.utils;

import cn.yshujia.config.MinioConfig;
import cn.yshujia.enums.MinioFolder;
import com.luciad.imageio.webp.WebPWriteParam;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.MemoryCacheImageOutputStream;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * @author yshujia
 * @create 2024/12/26
 * @description MinioUtils
 */

@Slf4j
@Component
public class MinioUtils {

	public static String STATIC_DOMAIN;
	private static MinioConfig config;
	private static MinioClient minioClient;

	@Autowired
	public MinioUtils(MinioConfig config, MinioClient minioClient) {
		MinioUtils.config = config;
		STATIC_DOMAIN = config.getDomain();
		MinioUtils.minioClient = minioClient;
	}

	public static String upload(MultipartFile file, MinioFolder folder) {
		byte[] bytes = createWebp(file);
		if (bytes == null) {
			return null;
		}
		String uniqueFileName = createUrl(folder.getFolder());
		if (uploadFile(bytes, uniqueFileName)) {
			return uniqueFileName;
		}
		return null;
	}

	public static List<String> upload(MultipartFile[] files, MinioFolder folder) {
		List<String> urls = new ArrayList<>();
		for (MultipartFile file : files) {
			String url = upload(file, folder);
			if (url != null) {
				urls.add(url);
			}
		}
		return urls;
	}


	/**
	 * @param file:   文件
	 * @param folder: 上传到的文件夹
	 *                [file, folder] * @return Map<String,String>
	 * @author yshujia
	 * @description 返回文件的 原文件名 和 url
	 * @create 2025/3/5 16:02
	 */
	public static Map<String, String> uploadNameUrl(MultipartFile file, MinioFolder folder) {
		String url = createUrl(folder.getFolder());
		byte[] bytes = createWebp(file);
		if (bytes == null) {
			return null;
		}
		Map<String, String> map = new HashMap<>();
		if (uploadFile(bytes, url)) {
			map.put(file.getOriginalFilename(), url);
			return map;
		}
		return null;
	}

	public static Map<String, String> uploadNameUrl(MultipartFile[] files, MinioFolder folder) {
		Map<String, String> urls = new HashMap<>();
		for (MultipartFile file : files) {
			Map<String, String> map = uploadNameUrl(file, folder);
			if (map != null) {
				urls.putAll(map);
			} else {
				delete(urls);
			}
		}
		return urls;
	}

	/**
	 * @param file:   文件
	 * @param folder: 上传到的文件夹
	 *                [file, folder] * @return Map<String,Double>
	 * @author yshujia
	 * @description 返回文件的 url 和 size
	 * @create 2025/3/4 15:58
	 */
	public static Map<String, Double> uploadUrlKb(MultipartFile file, MinioFolder folder) {
		return uploadFileMap(file, createUrl(folder.getFolder()));
	}

	public static Map<String, Double> uploadUrlKb(MultipartFile[] files, MinioFolder folder) {
		Map<String, Double> urls = new HashMap<>();
		for (MultipartFile file : files) {
			Map<String, Double> map = uploadUrlKb(file, folder);
			if (map != null) {
				urls.putAll(map);
			} else {
				delete(urls);
			}
		}
		return urls;
	}

	public static String createUrl(String folder) {
		return folder + "/" + TimeUtils.getParallelDate() + "/" + TimeUtils.getParallelTime() + ".webp";
	}

	private static String uploadFile(MultipartFile file) {
		byte[] bytes = createWebp(file);
		if (bytes == null) {
			return null;
		}

		String uniqueFileName = createUrl(file.getOriginalFilename());
		// 使用 try-with-resources 确保 InputStream 关闭
		if (uploadFile(bytes, uniqueFileName)) {
			return uniqueFileName;
		}
		return null;
	}

	private static Map<String, Double> uploadFileMap(MultipartFile file, String folderName) {
		byte[] bytes = createWebp(file);
		if (bytes == null) {
			return null;
		}
		Map<String, Double> map = new HashMap<>();
		if (uploadFile(bytes, folderName)) {
			map.put(folderName, bytes.length / 1024.0);
			return map;
		}
		return null;
	}

	private static boolean uploadFile(byte[] bytes, String uniqueFileName) {
		// 检查配置和文件名
		String bucketName = config.getBucketName();
		try {
			if (bucketName == null || bucketName.isEmpty()) {
				throw new IllegalArgumentException("Bucket name is not configured");
			}
			// 使用 try-with-resources 确保 InputStream 关闭
			InputStream inputStream = new ByteArrayInputStream(bytes);
			PutObjectArgs args = PutObjectArgs.builder().bucket(bucketName).object(uniqueFileName).stream(inputStream, bytes.length, -1).contentType("image/webp").build();
			minioClient.putObject(args);
			log.info("文件上传成功: {}", uniqueFileName);
			return true;
		} catch (Exception e) {
			log.error("文件上传错误: {}", e.getLocalizedMessage());
			return false;
		}
	}

	public static void delete(String path) {
		if (null == path) {
			return;
		}
		path = path.replaceAll(MinioUtils.STATIC_DOMAIN, "");
		RemoveObjectArgs args = RemoveObjectArgs.builder().bucket(config.getBucketName()).object(path).build();
		try {
			minioClient.removeObject(args);
		} catch (Exception e) {
			log.error("文件删除错误: {}", e.getLocalizedMessage());
		}
	}

	public static void delete(Collection<String> paths) {
		if (ObjectUtils.isEmpty(paths)) {
			return;
		}
		for (String path : paths) {
			delete(path);
		}
	}

	public static void delete(Map<String, ?> map) {
		if (null == map) {
			return;
		}
		Set<String> list = map.keySet();
		for (String path : list) {
			delete(path);
		}
	}

	private static byte[] createWebp(MultipartFile file) {
		if (file == null || file.isEmpty()) {
			throw new IllegalArgumentException("Input file is null or empty");
		}
		try (InputStream fis = file.getInputStream(); ByteArrayOutputStream bao = new ByteArrayOutputStream()) {

			BufferedImage image = ImageIO.read(fis);
			if (image == null) {
				throw new IllegalArgumentException("Failed to read the input image");
			}
			Iterator<ImageWriter> writers = ImageIO.getImageWritersByMIMEType("image/webp");
			if (!writers.hasNext()) {
				throw new IllegalStateException("No WebP ImageWriter found");
			}
			ImageWriter writer = writers.next();
			WebPWriteParam writeParam = getWebPWriteParam(file, writer);
			try (ImageOutputStream ios = new MemoryCacheImageOutputStream(bao)) {
				writer.setOutput(ios);
				writer.write(null, new IIOImage(image, null, null), writeParam);
			} finally {
				writer.dispose(); // 确保所有资源被释放
			}
			// 将字节数组转换为 InputStream 并返回
			return bao.toByteArray();
		} catch (IOException e) {
			return null;
		}
	}

	private static WebPWriteParam getWebPWriteParam(MultipartFile file, ImageWriter writer) {
		WebPWriteParam writeParam = new WebPWriteParam(writer.getLocale());
		writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
		boolean isWebp = Objects.requireNonNull(file.getOriginalFilename()).contains("webp");
		// 如果不是 webp 格式 并且超过大小 则使用有损压缩
		if (!isWebp && file.getSize() >= config.getMaxSize()) {
			// 设置有损压缩
			writeParam.setCompressionType(writeParam.getCompressionTypes()[WebPWriteParam.LOSSY_COMPRESSION]);
			// 设置压缩质量. 范围 0-1
			writeParam.setCompressionQuality(config.getCompressionQuality());
		}
		return writeParam;
	}

}
